/*
 * Copyright (c) 2008, Takashi Ito
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the authors nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "GenaFrame.h"

// begin wxGlade: ::extracode

// end wxGlade

#ifdef TANSU_CP_ENABLE_GENA
// TODO: fix me
#include "http-internal.h"
#endif /* TANSU_CP_ENABLE_GENA */


static void
timer_cb(int fd, short what, void *arg)
{
    ((GenaFrame*)arg)->Tick();
}

static void
sub_handler(struct evhttp_request *req, void *arg)
{
    ((GenaFrame*)arg)->GenaHandler(req, GenaFrame::GENA_SUB_TYPE_SUBSCRIBE);
}

static void
resub_handler(struct evhttp_request *req, void *arg)
{
    ((GenaFrame*)arg)->GenaHandler(req, GenaFrame::GENA_SUB_TYPE_RESUBSCRIBE);
}

static void
unsub_handler(struct evhttp_request *req, void *arg)
{
    ((GenaFrame*)arg)->GenaHandler(req, GenaFrame::GENA_SUB_TYPE_UNSUBSCRIBE);
}

static void gena_cb(struct evhttp_request *req, void *arg)
{
    ((GenaFrame*)arg)->HandleEvent(req);
}

GenaFrame::GenaFrame(struct event_base *evbase,
                     const struct tansu_cp_device *cp_device,
                     const struct tansu_cp_service *cp_service,
                     wxWindow* parent,
                     int id,
                     const wxString& title,
                     const wxPoint& pos,
                     const wxSize& size,
                     long style)
    : wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE)
    , cp_device(cp_device)
    , cp_service(cp_service)
    , http(NULL)
    , http_port(0)
    , conn(NULL)
    , m_timeout(0)
    , evbase(evbase)
{
    memset(&timeout_ev, 0, sizeof(timeout_ev));

    // begin wxGlade: GenaFrame::GenaFrame
    window_1 = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D|wxSP_BORDER);
    window_3 = new wxSplitterWindow(window_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D|wxSP_BORDER);
    notebook_1 = new wxNotebook(window_3, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
    notebook_1_pane_1 = new wxPanel(notebook_1, wxID_ANY);
    panel_1 = new wxScrolledWindow(notebook_1_pane_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
    window_4 = new wxSplitterWindow(window_3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D|wxSP_BORDER);
    window_4_pane_2 = new wxPanel(window_4, wxID_ANY);
    window_4_pane_1 = new wxPanel(window_4, wxID_ANY);
    window_2 = new wxSplitterWindow(window_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D|wxSP_BORDER);
    window_2_pane_2 = new wxScrolledWindow(window_2, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
    window_2_pane_1 = new wxPanel(window_2, wxID_ANY);
    frame_3_statusbar = CreateStatusBar(2, 0);
    label_1 = new wxStaticText(window_2_pane_1, wxID_ANY, wxT("Info"));
    list_ctrl_info = new wxListCtrl(window_2_pane_1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSUNKEN_BORDER);
    label_6 = new wxStaticText(window_2_pane_2, wxID_ANY, wxT("State"));
    text_ctrl_state = new wxTextCtrl(window_2_pane_2, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY|wxNO_BORDER);
    label_7 = new wxStaticText(window_2_pane_2, wxID_ANY, wxT("SID"));
    text_ctrl_sid = new wxTextCtrl(window_2_pane_2, wxID_ANY, wxEmptyString);
    button_1 = new wxButton(window_2_pane_2, ID_GENA_RESET, wxT("Reset"));
    label_8 = new wxStaticText(window_2_pane_2, wxID_ANY, wxT("Timeout"));
    text_ctrl_timeout = new wxTextCtrl(window_2_pane_2, wxID_ANY, wxEmptyString);
    button_2 = new wxButton(window_2_pane_2, ID_GENA_SUB, wxT("Sub"));
    button_3 = new wxButton(window_2_pane_2, ID_GENA_RESUB, wxT("Resub"));
    button_4 = new wxButton(window_2_pane_2, ID_GENA_UNSUB, wxT("Unsub"));
    label_5 = new wxStaticText(window_2_pane_2, wxID_ANY, wxT("Result"));
    list_ctrl_result = new wxListCtrl(window_2_pane_2, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSUNKEN_BORDER);
    list_ctrl_event = new wxListCtrl(window_4_pane_1, ID_GENA_EVENT_LIST, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSUNKEN_BORDER);
    const wxString *list_box_variable_choices = NULL;
    list_box_variable = new wxListBox(window_4_pane_2, ID_GENA_VAR_LIST, wxDefaultPosition, wxDefaultSize, 0, list_box_variable_choices, wxLB_HSCROLL);
    label_10 = new wxStaticText(panel_1, wxID_ANY, wxT("InstanceID"));
    label_11 = new wxStaticText(panel_1, wxID_ANY, wxT("Attribute"));
    const wxString *list_box_instance_choices = NULL;
    list_box_instance = new wxListBox(panel_1, ID_GENA_INSTANCE, wxDefaultPosition, wxDefaultSize, 0, list_box_instance_choices, 0);
    list_ctrl_attribute = new wxListCtrl(panel_1, ID_GENA_ATTRIBUTE, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxSUNKEN_BORDER);
    text_ctrl_value = new wxTextCtrl(notebook_1_pane_1, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL|wxTE_AUTO_URL);
    text_ctrl_xml = new wxTextCtrl(notebook_1, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL|wxTE_AUTO_URL);

    set_properties();
    do_layout();
    // end wxGlade

    events.DeleteContents(true);

    http = evhttp_new(evbase);
    if (http) {
        evhttp_bind_socket(http, "0.0.0.0", 0);
#ifdef TANSU_CP_ENABLE_GENA
        evhttp_set_cb(http, "/gena", gena_cb, this);

        http_port = GetPort(EVENT_FD(&TAILQ_FIRST(&http->sockets)->bind_ev));
#endif /* TANSU_CP_ENABLE_GENA */

        char *local_addr = GetOutAddress(eventSubAddr.ToAscii());
        if (local_addr) {
            callbackURL = _("<http://") + wxString::FromAscii(local_addr) + wxString::Format(_(":%d/gena>"), http_port);
            free(local_addr);
        } else {
            callbackURL = wxString::Format(_("<http://0.0.0.0:%d/gena>"), http_port);
        }

        list_ctrl_info->SetItem(idx_callbackURL, 1, callbackURL);
    }
}


GenaFrame::~GenaFrame()
{
    if (conn)
        evhttp_connection_free(conn);
    event_del(&timeout_ev);
    evhttp_free(http);
}


BEGIN_EVENT_TABLE(GenaFrame, wxFrame)
    // begin wxGlade: GenaFrame::event_table
    EVT_BUTTON(ID_GENA_RESET, GenaFrame::OnSidReset)
    EVT_BUTTON(ID_GENA_SUB, GenaFrame::OnSub)
    EVT_BUTTON(ID_GENA_RESUB, GenaFrame::OnResub)
    EVT_BUTTON(ID_GENA_UNSUB, GenaFrame::OnUnsub)
    EVT_LIST_ITEM_DESELECTED(ID_GENA_EVENT_LIST, GenaFrame::OnEventDeselected)
    EVT_LIST_ITEM_SELECTED(ID_GENA_EVENT_LIST, GenaFrame::OnEventSelected)
    EVT_LISTBOX(ID_GENA_VAR_LIST, GenaFrame::OnVarList)
    EVT_LISTBOX(ID_GENA_INSTANCE, GenaFrame::OnInstanceList)
    EVT_LIST_ITEM_DESELECTED(ID_GENA_ATTRIBUTE, GenaFrame::OnAttrDeselected)
    EVT_LIST_ITEM_SELECTED(ID_GENA_ATTRIBUTE, GenaFrame::OnAttrSelected)
    // end wxGlade
END_EVENT_TABLE();


void GenaFrame::OnSidReset(wxCommandEvent &event)
{
    event.Skip();
    text_ctrl_sid->SetValue(currentSID);
}


void GenaFrame::OnSub(wxCommandEvent &event)
{
    event.Skip();
    Execute(GENA_SUB_TYPE_SUBSCRIBE);
}


void GenaFrame::OnResub(wxCommandEvent &event)
{
    event.Skip();
    Execute(GENA_SUB_TYPE_RESUBSCRIBE);
}


void GenaFrame::OnUnsub(wxCommandEvent &event)
{
    event.Skip();
    Execute(GENA_SUB_TYPE_UNSUBSCRIBE);
}


void GenaFrame::OnEventDeselected(wxListEvent &event)
{
    event.Skip();
    SetEvent(NULL);
}


void GenaFrame::OnEventSelected(wxListEvent &event)
{
    event.Skip();

    long idx = event.GetIndex();
    if (idx >= 0)
        SetEvent(events.Item(idx)->GetData());
}


void GenaFrame::OnVarList(wxCommandEvent &event)
{
    event.Skip();

    int event_idx = UpnpFrame::GetSelectedListIndex(*list_ctrl_event);
    if (event_idx < 0)
        return;

    int idx = event.GetSelection();
    if (idx < 0)
        return;

    wxString str = list_box_variable->GetString(idx);
    struct tdom_doc *doc = events.Item(event_idx)->GetData()->GetDoc();

    struct tdom_elem *elem;
    struct tdom_elem *target;
    int n = tdom_count_elem(doc->root, "property", TANSU_NS_UPNP_EVENT);

    int i;
    for (i = 0; i < n; i++) {
        elem = tdom_get_elem(doc->root, "property", TANSU_NS_UPNP_EVENT, i);
        if ((target = tdom_get_elem(elem, str.ToUTF8(), NULL, 0)))
            break;
    }

    str = _("");
    if (target) {
        const char *tmp = tdom_get_value(target);
        if (tmp)
            str = wxString::FromUTF8(tmp);
    }
    text_ctrl_value->SetValue(str);
}


void GenaFrame::OnInstanceList(wxCommandEvent &event)
{
    event.Skip();
    printf("Event handler (GenaFrame::OnInstanceList) not implemented yet\n");
    fflush(stdout);
}


void GenaFrame::OnAttrDeselected(wxListEvent &event)
{
    event.Skip();
    printf("Event handler (GenaFrame::OnAttrDeselected) not implemented yet\n");
    fflush(stdout);
}


void GenaFrame::OnAttrSelected(wxListEvent &event)
{
    event.Skip();
    printf("Event handler (GenaFrame::OnAttrSelected) not implemented yet\n");
    fflush(stdout);
}


// wxGlade: add GenaFrame event handlers


void GenaFrame::set_properties()
{
    // begin wxGlade: GenaFrame::set_properties
    SetTitle(wxT("GENA Client"));
    SetSize(wxSize(800, 600));
    int frame_3_statusbar_widths[] = { 100, -1 };
    frame_3_statusbar->SetStatusWidths(2, frame_3_statusbar_widths);
    const wxString frame_3_statusbar_fields[] = {
        wxT("Event: "),
        wxEmptyString
    };
    for(int i = 0; i < frame_3_statusbar->GetFieldsCount(); ++i) {
        frame_3_statusbar->SetStatusText(frame_3_statusbar_fields[i], i);
    }
    window_2_pane_2->SetScrollRate(10, 10);
    panel_1->Hide();
    panel_1->SetScrollRate(10, 10);
    // end wxGlade

    text_ctrl_timeout->SetValue(_("300"));

    list_ctrl_info->InsertColumn(0, _("name"));
    list_ctrl_info->SetColumnWidth(0, 110);
    list_ctrl_info->InsertColumn(1, _("value"));
    list_ctrl_info->SetColumnWidth(1, 500);

    idx_udn = list_ctrl_info->InsertItem(0, _("UDN"));
    idx_fname = list_ctrl_info->InsertItem(1, _("friendlyName"));
    idx_dtype = list_ctrl_info->InsertItem(2, _("deviceType"));
    idx_stype = list_ctrl_info->InsertItem(3, _("serviceType"));
    idx_serviceId = list_ctrl_info->InsertItem(4, _("serviceId"));
    idx_eventSubURL = list_ctrl_info->InsertItem(5, _("eventSubURL"));
    idx_callbackURL = list_ctrl_info->InsertItem(6, _("callbackURL"));
    idx_sid = list_ctrl_info->InsertItem(7, _("Current SID"));
    idx_timeout = list_ctrl_info->InsertItem(8, _("Timeout"));

    list_ctrl_result->InsertColumn(0, _("Time"));
    list_ctrl_result->SetColumnWidth(0, 160);
    list_ctrl_result->InsertColumn(1, _("TAT"));
    list_ctrl_result->SetColumnWidth(1, 80);
    list_ctrl_result->InsertColumn(2, _("HTTP"));
    list_ctrl_result->SetColumnWidth(2, 50);
    list_ctrl_result->InsertColumn(3, _("SID"));
    list_ctrl_result->SetColumnWidth(3, 50);
    list_ctrl_result->InsertColumn(4, _("Timeout"));
    list_ctrl_result->SetColumnWidth(4, 80);
    list_ctrl_result->InsertColumn(5, _("Error Description"));
    list_ctrl_result->SetColumnWidth(5, 300);

    list_ctrl_event->InsertColumn(0, _("Time"));
    list_ctrl_event->SetColumnWidth(0, 160);
    list_ctrl_event->InsertColumn(1, _("SEQ"));
    list_ctrl_event->SetColumnWidth(1, 50);
    list_ctrl_event->InsertColumn(2, _("SID"));
    list_ctrl_event->SetColumnWidth(2, 200);

    SetValues();
}


void GenaFrame::do_layout()
{
    // begin wxGlade: GenaFrame::do_layout
    wxBoxSizer* sizer_1 = new wxBoxSizer(wxVERTICAL);
    wxFlexGridSizer* grid_sizer_8 = new wxFlexGridSizer(2, 1, 0, 0);
    wxFlexGridSizer* grid_sizer_9 = new wxFlexGridSizer(2, 2, 0, 0);
    wxFlexGridSizer* grid_sizer_7 = new wxFlexGridSizer(1, 1, 0, 0);
    wxBoxSizer* sizer_2 = new wxBoxSizer(wxVERTICAL);
    wxFlexGridSizer* grid_sizer_3 = new wxFlexGridSizer(3, 1, 0, 0);
    wxFlexGridSizer* grid_sizer_4 = new wxFlexGridSizer(3, 2, 0, 0);
    wxFlexGridSizer* grid_sizer_6 = new wxFlexGridSizer(1, 5, 0, 0);
    wxFlexGridSizer* grid_sizer_5 = new wxFlexGridSizer(1, 2, 0, 0);
    wxFlexGridSizer* grid_sizer_1 = new wxFlexGridSizer(2, 1, 0, 0);
    grid_sizer_1->Add(label_1, 0, 0, 0);
    grid_sizer_1->Add(list_ctrl_info, 1, wxEXPAND, 0);
    window_2_pane_1->SetSizer(grid_sizer_1);
    grid_sizer_1->AddGrowableRow(1);
    grid_sizer_1->AddGrowableCol(0);
    grid_sizer_4->Add(label_6, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_4->Add(text_ctrl_state, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_4->Add(label_7, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_5->Add(text_ctrl_sid, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_5->Add(button_1, 0, 0, 0);
    grid_sizer_5->AddGrowableCol(0);
    grid_sizer_4->Add(grid_sizer_5, 1, wxEXPAND, 0);
    grid_sizer_4->Add(label_8, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_6->Add(text_ctrl_timeout, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_6->Add(0, 0, 0, wxEXPAND, 0);
    grid_sizer_6->Add(button_2, 0, 0, 0);
    grid_sizer_6->Add(button_3, 0, 0, 0);
    grid_sizer_6->Add(button_4, 0, 0, 0);
    grid_sizer_6->AddGrowableCol(1);
    grid_sizer_4->Add(grid_sizer_6, 1, wxEXPAND, 0);
    grid_sizer_4->AddGrowableRow(0);
    grid_sizer_4->AddGrowableCol(0);
    grid_sizer_4->AddGrowableCol(1);
    grid_sizer_3->Add(grid_sizer_4, 1, wxEXPAND, 0);
    grid_sizer_3->Add(label_5, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_3->Add(list_ctrl_result, 1, wxEXPAND, 0);
    window_2_pane_2->SetSizer(grid_sizer_3);
    grid_sizer_3->AddGrowableRow(2);
    grid_sizer_3->AddGrowableCol(0);
    window_2->SplitVertically(window_2_pane_1, window_2_pane_2);
    sizer_2->Add(list_ctrl_event, 1, wxEXPAND, 0);
    window_4_pane_1->SetSizer(sizer_2);
    grid_sizer_7->Add(list_box_variable, 0, wxEXPAND, 0);
    window_4_pane_2->SetSizer(grid_sizer_7);
    grid_sizer_7->AddGrowableRow(0);
    grid_sizer_7->AddGrowableCol(0);
    window_4->SplitHorizontally(window_4_pane_1, window_4_pane_2);
    grid_sizer_9->Add(label_10, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_9->Add(label_11, 0, wxALIGN_CENTER_VERTICAL, 0);
    grid_sizer_9->Add(list_box_instance, 0, wxEXPAND, 0);
    grid_sizer_9->Add(list_ctrl_attribute, 1, wxEXPAND, 0);
    panel_1->SetSizer(grid_sizer_9);
    grid_sizer_9->AddGrowableRow(1);
    grid_sizer_9->AddGrowableCol(1);
    grid_sizer_8->Add(panel_1, 1, wxEXPAND, 0);
    grid_sizer_8->Add(text_ctrl_value, 0, wxEXPAND, 0);
    notebook_1_pane_1->SetSizer(grid_sizer_8);
    grid_sizer_8->AddGrowableRow(0);
    grid_sizer_8->AddGrowableRow(1);
    grid_sizer_8->AddGrowableCol(0);
    notebook_1->AddPage(notebook_1_pane_1, wxT("Value"));
    notebook_1->AddPage(text_ctrl_xml, wxT("Text"));
    window_3->SplitVertically(window_4, notebook_1);
    window_1->SplitHorizontally(window_2, window_3);
    sizer_1->Add(window_1, 1, wxEXPAND, 0);
    SetSizer(sizer_1);
    Layout();
    // end wxGlade
}


void GenaFrame::SetValues()
{
    if (cp_device == NULL || cp_service == NULL)
        return;
    if (cp_device->dd == NULL || cp_service->scpd == NULL)
        return;

    const char *tmp;
    struct tdom_elem *elem;

    wxString udn(cp_device->udn, wxConvUTF8);
    list_ctrl_info->SetItem(idx_udn, 1, udn);

    struct tdom_elem *device;
    device = cp_device->embedded ? cp_device->embedded : cp_device->rootdevice;
    if (device == NULL)
        return;

    tmp = tansu_dd_get_elem_value(device, "deviceType");
    if (tmp)
        list_ctrl_info->SetItem(idx_dtype, 1, wxString::FromUTF8(tmp));

    tmp = tansu_dd_get_elem_value(device, "friendlyName");
    if (tmp)
        list_ctrl_info->SetItem(idx_fname, 1, wxString::FromUTF8(tmp));

    serviceType = wxString::FromUTF8(cp_service->type);
    list_ctrl_info->SetItem(idx_stype, 1, serviceType);
    list_ctrl_info->SetItem(idx_serviceId, 1, wxString::FromUTF8(cp_service->sid));

    const char *base = tansu_dd_get_elem_value(cp_device->dd->root, "URLBase");

    elem = tansu_dd_get_service(device, cp_service->sid);
    tmp = tansu_dd_get_elem_value(tansu_dd_get_service(device, cp_service->sid), "eventSubURL");
    if (tmp) {
        char *abs = tansu_url_get_abs(cp_device->location, base, tmp);
        if (abs) {
            eventSubURL = wxString::FromAscii(abs);
            list_ctrl_info->SetItem(idx_eventSubURL, 1, eventSubURL);
            free(abs);
            char *address;
            uint16_t port;
            if (tansu_url_parse(abs, NULL, NULL, NULL, &address, &port, &tmp) == TANSU_SUCCESS) {
                eventSubPath = wxString::FromAscii(tmp);
                eventSubAddr = wxString::FromAscii(address);
                eventSubHost = eventSubAddr + wxString::Format(_(":%d"), port);
                eventSubPort = port;
                free(address);
            }
        }
    }
}

void GenaFrame::SetEvent(GenaEvent *event)
{
    list_box_variable->Set(0, NULL);
    list_box_instance->Set(0, NULL);

    if (event == NULL)
        return;

    struct tdom_doc *doc = event->GetDoc();
    SoapFrame::SetXML(*text_ctrl_xml, doc, event->GetHeaders());

    int n = tdom_count_elem(doc->root, "property", TANSU_NS_UPNP_EVENT);

    wxArrayString names;
    struct tdom_elem *elem;

    int i;
    for (i = 0; i < n; i++) {
        elem = tdom_get_elem(doc->root, "property", TANSU_NS_UPNP_EVENT, i);
        if (elem) {
            struct tdom_node *node = TAILQ_FIRST(&elem->children);
            if (node && node->type == TDOM_ELEM)
                names.Add(wxString::FromAscii(node->name));
        }
    }

    // TODO: LastChange

    list_box_variable->Set(names);
}

void GenaFrame::SetCurrent(const char *sid, const char *timeout)
{
    if (sid == NULL)
        sid = "";
    if (timeout == NULL)
        timeout = "";

    currentSID = wxString::FromUTF8(sid);
    text_ctrl_sid->SetValue(currentSID);
    list_ctrl_info->SetItem(idx_sid, 1, currentSID);

    wxString tmout = wxString::FromUTF8(timeout);
    list_ctrl_info->SetItem(idx_timeout, 1, tmout);
    char *endptr;
    int n = strtol(timeout, &endptr, 10);
    event_del(&timeout_ev);
    m_timeout = 0;
    if (*endptr == '\0' && n > 0) {
        m_timeout = n;
        struct timeval tv = {1, 0};
        evtimer_assign(&timeout_ev, evbase, timer_cb, this);
        event_add(&timeout_ev, &tv);
    }
}

void GenaFrame::Execute(GenaSubType type)
{
    if (conn)
        return;

    void (*func)(struct evhttp_request *, void *) = NULL;
    if (type == GENA_SUB_TYPE_SUBSCRIBE)
        func = sub_handler;
    else if (type == GENA_SUB_TYPE_RESUBSCRIBE)
        func = resub_handler;
    else if (type == GENA_SUB_TYPE_UNSUBSCRIBE)
        func = unsub_handler;
    else
        return;

    // get local address
    char *local_addr = GetOutAddress(eventSubAddr.ToAscii());
    wxString cburl;
    if (local_addr == NULL)
        return;
    cburl = _("<http://") + wxString::FromUTF8(local_addr) + wxString::Format(_(":%d/gena>"), http_port);
    free(local_addr);
    callbackURL = cburl;
    list_ctrl_info->SetItem(idx_callbackURL, 1, cburl);

    // get params
    wxString sid = text_ctrl_sid->GetValue();
    wxString timeout = _("Second-") + text_ctrl_timeout->GetValue();

    // create request
    conn = evhttp_connection_base_new(evbase, NULL, eventSubAddr.ToAscii(), eventSubPort);
    if (conn == NULL)
        return;

    struct evhttp_request *req;
    if ((req = evhttp_request_new(func, this)) == NULL) {
        evhttp_connection_free(conn);
        conn = NULL;
        return;
    }

    // make header
    enum evhttp_cmd_type cmd_type = EVHTTP_REQ_GET;
    evhttp_add_header(req->output_headers, "HOST", eventSubHost.ToAscii());
    if (type == GENA_SUB_TYPE_SUBSCRIBE) {
#ifdef TANSU_CP_ENABLE_GENA
        cmd_type = EVHTTP_REQ_SUB;
#endif
        evhttp_add_header(req->output_headers, "CALLBACK", cburl.ToAscii());
        evhttp_add_header(req->output_headers, "NT", "upnp:event");
        evhttp_add_header(req->output_headers, "TIMEOUT", timeout.ToAscii());
    } else if (type == GENA_SUB_TYPE_RESUBSCRIBE) {
#ifdef TANSU_CP_ENABLE_GENA
        cmd_type = EVHTTP_REQ_SUB;
#endif
        evhttp_add_header(req->output_headers, "SID", sid.ToAscii());
        evhttp_add_header(req->output_headers, "TIMEOUT", timeout.ToAscii());
    } else if (type == GENA_SUB_TYPE_UNSUBSCRIBE) {
#ifdef TANSU_CP_ENABLE_GENA
        cmd_type = EVHTTP_REQ_UNSUB;
#endif
        evhttp_add_header(req->output_headers, "SID", sid.ToAscii());
    }
    evhttp_add_header(req->output_headers, "Connection", "close");

    if (evhttp_make_request(conn, req, cmd_type, eventSubPath.ToAscii()) != 0) {
        evhttp_request_free(req);
        evhttp_connection_free(conn);
        conn = NULL;
        return;
    }

    time_t t;
    time(&t);
    wxString str = UpnpFrame::GetTime(&t);
    long m = list_ctrl_result->InsertItem(0, str);
    list_ctrl_result->SetItemTextColour(m, *wxLIGHT_GREY);
}

uint16_t GenaFrame::GetPort(int fd)
{
    struct sockaddr_storage ss;
    socklen_t len;

    len = sizeof(ss);
    if (getsockname(fd, (struct sockaddr *)&ss, &len) == -1)
        return 0;

    if (len == sizeof(struct sockaddr_in))
        return ntohs(((struct sockaddr_in *)&ss)->sin_port);

    if (len == sizeof(struct sockaddr_in6))
        return ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);

    return 0;
}


char *GenaFrame::GetOutAddress(const char *remote)
{
    tansu_status_t status;
    struct sockaddr_storage ss;

    if (remote == NULL)
        return NULL;

    status = tansu_socket_set_addr(AF_INET, &ss, remote, 1024);
    if (status != TANSU_SUCCESS) {
        status = tansu_socket_set_addr(AF_INET6, &ss, remote, 1024);
        if (status != TANSU_SUCCESS)
            return NULL;
    }

    return tansu_socket_getlocal(&ss);
}

void GenaFrame::Tick()
{
    m_timeout--;
    wxString str = wxString::Format(_("%d"), m_timeout);

    list_ctrl_info->SetItem(idx_timeout, 1, str);

    if (m_timeout > 0) {
        struct timeval tv = {1, 0};
        evtimer_assign(&timeout_ev, evbase, timer_cb, this);
        event_add(&timeout_ev, &tv);
    } else {
        // TODO: auto resub
    }
}

void GenaFrame::HandleEvent(struct evhttp_request *req)
{
    if (req == NULL)
        return;
#ifdef TANSU_CP_ENABLE_GENA
    if (req->type != EVHTTP_REQ_NOTIFY)
        return;
#endif

    time_t t;
    time(&t);
    wxString str = UpnpFrame::GetTime(&t);

    int n = list_ctrl_event->GetItemCount();
    long m = list_ctrl_event->InsertItem(n, str);

    const char *tmp;
    if ((tmp = evhttp_find_header(req->input_headers, "SEQ")))
        list_ctrl_event->SetItem(m, 1, wxString::FromAscii(tmp));
    if ((tmp = evhttp_find_header(req->input_headers, "SID")))
        list_ctrl_event->SetItem(m, 2, wxString::FromAscii(tmp));

    struct tdom_doc *doc = tansu_xml_parse_evbuffer(req->input_buffer);
    GenaEvent *event = new GenaEvent(doc, req->input_headers);
    events.Append(event);

    evhttp_send_reply(req, HTTP_OK, "OK", NULL);
}

void GenaFrame::GenaHandler(struct evhttp_request *req, GenaSubType type)
{
    const char *tmp;
    int status_code = -1;
    const wxColour *color = wxBLACK;
    wxString readable = wxEmptyString;
    wxString sid = wxEmptyString;
    wxString timeout = wxEmptyString;

    if (req == NULL) {
        color = wxRED;
    } else {
        status_code = req->response_code;
        if (req->response_code_line)
            readable = wxString::FromAscii(req->response_code_line);

        if (status_code == HTTP_OK) {
            if ((tmp = evhttp_find_header(req->input_headers, "SID")))
                sid = wxString::FromUTF8(tmp);
            if ((tmp = evhttp_find_header(req->input_headers, "TIMEOUT")))
                if (strncmp(tmp, "Second-", 7) == 0)
                    timeout = wxString::FromUTF8(&tmp[7]);
        }
    }

    list_ctrl_result->SetItem(0, 2, wxString::Format(_("%d"), status_code));
    list_ctrl_result->SetItem(0, 3, sid);
    list_ctrl_result->SetItem(0, 4, timeout);
    list_ctrl_result->SetItem(0, 5, readable);
    list_ctrl_result->SetItemTextColour(0, *color);

    if (type == GENA_SUB_TYPE_UNSUBSCRIBE) {
        SetCurrent(NULL, NULL);
    } else {
        if (sid.Len() > 0) {
            SetCurrent(sid.ToAscii(), timeout.ToAscii());
        }
    }

    evhttp_connection_free(conn);
    conn = NULL;
}

#include <wx/listimpl.cpp>
WX_DEFINE_LIST(GenaEventList);

